9장. 임베딩 모델 길들이기

출처: 『RAG 마스터: 랭체인으로 완성하는 LLM 서비스』(브라이스 유·조경아·박수진·김재웅 지음, 프리렉 2025) | 공식: www.langchain.com

코드는 분위기만 — trainer.train()·scale=20·@5 같은 말은 몰라도 됩니다. 표의 '비유'와 '위험'만 봐도 충분해요.

이 장은 임베딩 모델을 우리 문서에 맞게 다시 가르치는 이야기다.

0장에서 임베딩은 "글을 숫자 묶음으로 바꾸는 일"이라 했다.

그 변환기를 우리 분야 전용으로 손보는 게 이 장의 전부다.


0. 이 장의 새 단어 (0장에 없는 말만)

0장 용어집에 이미 있는 말(임베딩·벡터·코사인 유사도·청크·검색기·파인튜닝)은 그대로 쓴다.

여기 3개만 이 장에서 처음 나온다.


대조 학습 (contrastive learning)

한 문장 뜻 — "이건 가까이, 저건 멀리" 하고 짝을 대조시켜 가며 가르치는 방식.

일상비유 — 아이에게 사진을 보여주며 "이 둘은 같은 강아지, 이 둘은 다른 동물" 하고 짝지어 알려주는 것. 같은 건 묶고 다른 건 떼면서 눈을 키운다.

한 줄 예 —

# 아래 예제는 핵심 흐름만 짧게 보여 줍니다.
# (가까워야 할 짝, 멀어야 할 짝)을 잔뜩 주고 반복 학습
# 모델이 "비슷한 글은 숫자도 비슷하게" 찍는 눈을 기른다

앵커 (anchor)

한 문장 뜻 — 짝을 만들 때 기준이 되는 글. 보통 사용자가 던질 검색어다.

일상비유 — 소개팅 주선의 주인공. 이 사람을 가운데 두고 "잘 맞는 사람(가까이)" "안 맞는 사람(멀리)"을 양옆에 세운다.

한 줄 예 —

# 아래 예제는 핵심 흐름만 짧게 보여 줍니다.
앵커 = "서울 인구는?"        # 기준 글 (검색어)
가까운짝 = "서울 인구는 약 970만 명입니다."  # 앵커와 관련 있음
먼짝   = "파리는 프랑스의 수도입니다."        # 앵커와 관련 없음

배치 (batch)

한 문장 뜻 — 모델이 한 번에 묶어서 학습하는 데이터 한 덩어리.

일상비유 — 빨래를 한 통씩 돌리는 것. 5천 장을 한 번에 못 넣으니 40장씩 묶어 여러 번 돌린다. 그 한 통이 배치다.

한 줄 예 —

# 아래 예제는 핵심 흐름만 짧게 보여 줍니다.
# 데이터 5,000개를 40개씩 묶으면 → 총 125번 학습
batch_size = 40   # 한 통에 40개씩

(귀납 도입) 이런 적 있죠?

회사에 의료 RAG를 깔았다.

"심근경색 초기 증상은?" 물었더니, 엉뚱한 피부질환 문서를 찾아왔다.

분명 0장에서 배운 대로 글을 숫자로 바꿔 비슷한 걸 찾게 했는데도 말이다.

문제는 그 숫자 변환기(임베딩 모델)에 있다.

이 변환기는 인터넷 글 전반으로 배웠다.

뉴스, 블로그, 잡담을 두루 안다.

하지만 우리 의료 전문 문서는 본 적이 없다.

그래서 "심근경색"과 "협심증"의 미묘한 차이를 못 잡는다.

일반인 눈엔 둘 다 그냥 "가슴 아픈 병"으로 뭉뚱그려지는 것과 같다.

# 일반 임베딩 모델: 전문 분야에선 헷갈림
# 문장이나 문서를 의미 벡터로 바꾸는 임베딩 단계를 준비합니다.
vec1 = embeddings.embed_query("심근경색 증상")
# 문장이나 문서를 의미 벡터로 바꾸는 임베딩 단계를 준비합니다.
vec2 = embeddings.embed_query("피부 발진 증상")
# 둘이 너무 가깝게 찍혀서 → 엉뚱한 문서를 끌어옴

해결책은 이 변환기를 우리 분야 전용으로 다시 가르치는 것이다.

신입 사원에게 우리 회사 일을 한 달 재교육시키듯 말이다.

그게 바로 임베딩 모델 파인튜닝이다.

사이드박스 — 파인튜닝은 0장에서 봤죠 (흥미용)

0장 용어집에서 파인튜닝을 "RAG의 대안"이라 했다. 그건 LLM(답 쓰는 모델) 자체를 재교육하는 이야기였다. 이 장의 파인튜닝은 대상이 다르다. 답 쓰는 모델이 아니라, 검색용 숫자 변환기(임베딩 모델)를 손본다. RAG를 버리는 게 아니라, RAG의 검색 부품을 더 똑똑하게 만드는 일이다.


이 장에서 딱 5가지만

  1. 왜 손보나 — 일반 임베딩 모델은 전문 분야 글에서 검색이 헛다리를 짚는다. 그래서 우리 문서로 다시 가르친다.
  2. 어떻게 가르치나 — "이건 가까이, 저건 멀리" 짝을 대조시켜 가르친다(대조 학습). 가까운 짝이 포지티브, 먼 짝이 네거티브다.
  3. 먼 짝은 공짜로 — 먼 짝(네거티브)을 일일이 만들지 않아도, 한 통(배치) 안의 남의 짝을 자동으로 빌려 쓴다(배치 내 네거티브 샘플링).
  4. 짝은 AI가 만든다 — 우리 문서에서 "이런 질문 나오겠다"를 GPT가 자동으로 뽑아 짝을 만들어 준다(합성 데이터).
  5. 좋아졌는지 숫자로 확인 — 손보기 전후의 검색 성적을 점수(Recall·MRR 등)로 재서 비교한다.

각 꼭지를 하나씩 풀어 본다.


개념 1 — 임베딩 학습의 목표: 가까운 건 가깝게, 먼 건 멀게

망가지는 장면

"서울 인구는?"이라는 질문에, 모델이 "파리는 프랑스 수도"라는 글을 가깝게 찍었다.

전혀 관련 없는데도 숫자가 비슷하게 나온 것이다.

이러면 검색기가 엉뚱한 문서를 꺼내 온다.

일상비유

도서관 사서가 책을 정리하는 모습.

요리책끼리는 같은 칸(가까이), 요리책과 자동차책은 다른 칸(멀리)에 꽂는다.

좋은 사서는 비슷한 책을 잘 모으고, 다른 책을 잘 떼어 놓는다.

임베딩 학습은 모델에게 이 사서의 눈을 길러 주는 일이다.

비유 코드 위험
비슷한 책 같은 칸에 embed("서울 인구") ↔ embed("서울은 970만") 가깝게 못 배우면 관련 글이 멀리 찍힘
다른 책 다른 칸에 embed("서울 인구") ↔ embed("파리는 수도") 멀게 못 배우면 무관한 글이 가깝게 찍힘

한 문장 정의 — 임베딩 학습의 목표는 의미가 비슷한 글은 숫자(벡터)도 가깝게, 다른 글은 멀게 찍히도록 모델을 길들이는 것이다.

예시 폭격

예시 1 (완성예) — 가까워야 하는 짝

# `앵커`에 중간 결과를 담아 다음 줄에서 재사용합니다.
앵커     = "강아지를 기르는 방법"
가까운짝 = "반려견 양육 가이드"   # 같은 주제 → 가깝게 찍혀야 함

예시 2 (부분완성) — 빈칸을 채워 보자. 아래 앵커에 짝을 하나 넣는다면?

# `앵커`에 중간 결과를 담아 다음 줄에서 재사용합니다.
앵커  = "파이썬 코딩 튜토리얼"
먼짝 = "________________"   # 힌트: 완전 다른 주제 (예: 고양이 사료 추천)

예시 3 (독립적용) — "당뇨 식단"이 앵커일 때, 가까운 짝과 먼 짝을 직접 떠올려 보자. - 가까운 짝: 당뇨 환자 식이요법 안내 (같은 주제) - 먼 짝: 오늘 축구 경기 결과 (무관한 주제)


개념 2 — 대조 학습: 좋은 짝·나쁜 짝으로 가르치기

망가지는 장면

모델에게 "이건 비슷해"라고 좋은 예만 잔뜩 보여줬다.

그랬더니 모든 글을 다 "비슷하다"고 찍어 버린다.

나쁜 예(달라야 하는 짝)를 안 보여줬더니, 구분하는 법을 못 배운 것이다.

일상비유

아이에게 동물을 가르칠 때.

"이 둘은 같은 강아지야"만 보여주면, 아이는 모든 네발짐승을 강아지라 부른다.

"이건 고양이, 강아지랑 달라"까지 보여줘야 비로소 구분한다.

대조 학습은 같은 짝과 다른 짝을 나란히 대조시켜 가르친다.

비유 코드 위험
"이 둘은 같아" (포지티브) (앵커, 가까운짝) 거리 좁히기 좋은 예만 주면 다 비슷하다 찍음
"이건 달라" (네거티브) (앵커, 먼짝) 거리 벌리기 나쁜 예 없으면 구분 못 배움

한 문장 정의 — 대조 학습은 앵커를 기준으로 가까워야 할 짝(포지티브)멀어야 할 짝(네거티브)을 함께 보여주며, 모델이 둘을 구분하도록 길들이는 방식이다.

한 걸음 더 ▸ (지금 몰라도 됨)

대조 학습은 보통 3단계로 돈다. ① 가까운 짝·먼 짝 준비 → ② 가까운 건 좁히고 먼 건 벌림 → ③ 그 좁히고 벌린 정도를 점수로 매겨 모델을 살짝씩 고침. 지금은 "같은 짝·다른 짝으로 대조시킨다"만 알면 된다.

예시 폭격

예시 1 (완성예) — 포지티브 짝 (가까워야 함)

# (앵커: 검색어, 포지티브: 관련 있는 문서)
# 이 줄은 예제 흐름을 이루는 실제 처리 단계입니다.
("서울의 인구는?", "서울의 인구는 약 970만 명입니다.")

예시 2 (완성예) — 네거티브 짝 (멀어야 함)

# 앵커는 같지만, 짝은 무관한 문서
# 이 줄은 예제 흐름을 이루는 실제 처리 단계입니다.
("서울의 인구는?", "파리는 프랑스의 수도입니다.")

예시 3 (독립적용) — "머신러닝이란?"이 앵커일 때 - 포지티브: "기계학습은 데이터로부터 패턴을 찾는 AI 기술입니다." (관련 있음) - 네거티브: "이 식당의 불고기가 맛있습니다." (완전 무관)


개념 3 — 데이터 짝 만들기: 트리플렛과 다중 네거티브

망가지는 장면

먼 짝을 만들려고 책상에 앉았다.

"무관한 문장"이면 다 될 줄 알았는데, 막상 고르려니 헷갈린다.

너무 동떨어진 문장은 모델이 너무 쉽게 구분해서 배울 게 없다.

너무 비슷한 문장은 사람도 헷갈려서 오히려 학습을 망친다.

먼 짝 고르기가 이 작업에서 제일 까다롭다.

일상비유

운동 코치가 난이도를 정하는 것.

너무 가벼운 아령은 들어도 근육이 안 붙는다.

너무 무거운 아령은 자세가 망가진다.

적당히 버거운 무게여야 실력이 는다.

먼 짝도 "너무 쉽지도, 너무 어렵지도 않은" 난이도가 좋다.

비유 코드 위험
한 묶음에 3개 (트리플렛) (앵커, 가까운짝, 먼짝) 매 줄 먼 짝을 손으로 골라야 함
먼 짝 여러 개 (다중) negatives: [먼짝1, 먼짝2, ...] 짝이 폭증해 만들 데이터가 산더미

한 문장 정의 — 학습 데이터는 보통 (앵커, 가까운짝, 먼짝) 세 개 묶음(트리플렛)이나 먼 짝을 여러 개 붙인 형태로 만들며, 적당한 난이도의 먼 짝 고르기가 가장 손이 많이 가는 일이다.

예시 폭격

예시 1 (완성예) — 트리플렛 한 줄

# (앵커, 가까운짝, 먼짝)
# 이 줄은 예제 흐름을 이루는 실제 처리 단계입니다.
("강아지 기르는 법", "반려견 양육 가이드", "고양이 사료 추천")

예시 2 (완성예) — 먼 짝을 여러 개 붙인 다중 구성

# 이 줄은 예제 흐름을 이루는 실제 처리 단계입니다.
{
  # 이 줄은 예제 흐름을 이루는 실제 처리 단계입니다.
  "anchor":   "머신러닝이란?",
  # 이 줄은 예제 흐름을 이루는 실제 처리 단계입니다.
  "positive": "기계학습은 데이터로 패턴을 찾는 AI 기술입니다.",
  # 이 줄은 예제 흐름을 이루는 실제 처리 단계입니다.
  "negatives": ["오늘 날씨가 좋네요.", "내일 회의는 2시입니다."]
}

예시 3 (독립적용) — 먼 짝 난이도 고르기. 앵커가 "비타민C 효능"이라면? - 너무 쉬운 먼 짝: "자동차 엔진 오일 교체" (배울 게 거의 없음) - 적당한 먼 짝: "비타민D의 효능" (주제는 비슷, 정답은 아님 → 좋은 난이도)


개념 4 — 배치 내 네거티브 샘플링: 먼 짝을 공짜로 빌리기

망가지는 장면

먼 짝 만들기가 너무 힘들다는 걸 깨달았다.

데이터 한 줄마다 무관한 문장을 손으로 골라야 하니, 수천 줄이면 수천 번 골라야 한다.

이대로면 시작도 못 한다.

일상비유

소개팅 단체 미팅장을 떠올려 보자.

네 쌍이 한 방에 모였다.

1번 주인공의 짝은 1번 파트너다.

그런데 같은 방에 있는 2·3·4번 파트너는 1번 주인공에겐 자동으로 "안 맞는 사람(먼 짝)"이 된다.

따로 먼 짝을 데려올 필요 없이, 옆자리 사람을 빌려 쓰는 셈이다.

비유 코드 위험
옆자리 짝을 먼 짝으로 빌림 한 배치의 남의 포지티브 = 내 네거티브 한 통 안에 같은 주제 섞이면 오해
가까운 짝만 챙기면 됨 (앵커, 가까운짝)만 준비 먼 짝 안 주면 손으로 만들 일 없음

한 문장 정의 — 배치 내 네거티브 샘플링은 한 통(배치) 안의 다른 짝들을 자동으로 먼 짝으로 빌려 쓰는 방식이라, 사용자는 가까운 짝(포지티브)만 준비하면 된다.

예시 폭격

예시 1 (완성예) — 배치 크기 4. 가까운 짝만 넣는다.

# `배치`에 중간 결과를 담아 다음 줄에서 재사용합니다.
배치 = [
  ("AI란?",      "AI는 인간 지능을 모방한 기술입니다."),  # 짝1
  ("딥러닝이란?", "신경망을 여러 층 쌓아 학습하는 방법입니다."),  # 짝2
  ("Python은?",  "데이터 분석·웹·AI에 널리 쓰입니다."),    # 짝3
  ("자연어처리?", "컴퓨터가 사람 언어를 다루는 분야입니다."), # 짝4
]

예시 2 (worked) — "AI란?"(앵커) 입장에서 자동 배정되는 짝

# 아래 예제는 핵심 흐름만 짧게 보여 줍니다.
# 가까운 짝(포지티브): "AI는 인간 지능을 모방한 기술입니다."
# 먼 짝(자동 네거티브): 나머지 짝2·짝3·짝4의 문서들
#   → "신경망을...", "데이터 분석...", "컴퓨터가..."

예시 3 (독립적용) — 배치에 5개 짝이 있다면, 한 앵커의 자동 먼 짝은 몇 개? - 답: 자기 짝 1개를 뺀 나머지 4개. (배치 크기 - 1)


개념 5 — 손실 함수: 잘 가르쳤는지 점수 매기기

망가지는 장면

모델을 가르치긴 하는데, 얼마나 틀렸는지 알 길이 없다.

채점 기준이 없으니 모델을 어느 방향으로 고쳐야 할지 모른다.

일상비유

시험 채점표.

채점표가 있어야 "여기서 5점 깎였네, 이걸 고치자"가 된다.

손실 함수는 모델의 오답 채점표다.

점수(손실)가 클수록 많이 틀린 거고, 모델은 이 점수를 줄이는 쪽으로 자신을 고친다.

이 장에서 쓰는 채점표 이름은 MultipleNegativesRankingLoss다.

이름은 길지만 하는 일은 하나다 — "가까운 짝은 더 가깝게, 먼 짝은 더 멀게" 못 했으면 점수를 깎는다.

비유 코드 위험
오답 채점표 loss = MultipleNegativesRankingLoss(model) 채점표 없으면 고칠 방향 모름
점수 줄이는 쪽으로 고침 손실 ↓ = 가까운 짝 ↑, 먼 짝 ↓ 점수 안 줄면 학습 멈춤

한 문장 정의 — 손실 함수는 모델이 얼마나 틀렸는지를 점수로 매기는 채점표이고, MultipleNegativesRankingLoss는 "가까운 짝 유사도는 높이고 먼 짝 유사도는 낮추도록" 점수를 매기는 채점표다.

한 걸음 더 ▸ (지금 몰라도 됨)

이 채점표에는 scale(스케일)이라는 손잡이가 있다(기본 20). 이 손잡이를 키우면 채점이 더 빡세진다 — 조금만 틀려도 점수를 크게 깎는다. 0장에서 본 온도(temperature)의 반대 개념인데, 지금은 "강도 손잡이가 있다"만 알면 된다. 수식이나 손잡이 값은 몰라도 검색은 잘 돈다.

예시 폭격

예시 1 (완성예) — 채점표 한 줄로 켜기

# 필요한 라이브러리나 클래스를 가져와서 아래 예제에서 쓸 준비를 합니다.
from sentence_transformers import losses
# `loss`에 중간 결과를 담아 다음 줄에서 재사용합니다.
loss = losses.MultipleNegativesRankingLoss(model)
# 이제 학습할 때 이 채점표로 오답을 잰다

예시 2 (개념) — 채점표가 원하는 방향

# 아래 예제는 핵심 흐름만 짧게 보여 줍니다.
# 잘 배운 상태:
#   sim(앵커, 가까운짝) = 0.9  (높음, 좋음)
#   sim(앵커, 먼짝)     = 0.1  (낮음, 좋음)
# → 손실 점수가 작다 = 잘 배웠다

예시 3 (독립적용) — 아래 둘 중 손실 점수가 더 큰(더 못 배운) 쪽은? - A: 가까운 짝 0.8, 먼 짝 0.2 → 잘 나뉨 - B: 가까운 짝 0.5, 먼 짝 0.5 → 안 나뉨 (← 손실 점수가 더 큼)


개념 6 — 학습 코드: 모델 불러와 가르치기

망가지는 장면

원리는 알겠는데, 실제로 무슨 코드를 쳐야 모델이 학습되는지 막막하다.

부품이 흩어져 있어 순서를 못 잡는다.

일상비유

요리 레시피.

재료(모델·데이터)를 준비하고, 도구(채점표)를 챙기고, 불에 올리면(학습) 완성이다.

순서만 잡으면 누구나 따라 한다.

비유 코드 위험
재료 사 오기 model = SentenceTransformer('BAAI/bge-m3') 엉뚱한 모델 고르면 한국어 약함
불에 올리기 trainer.train() 너무 오래 가르치면 원래 실력 망가짐

한 문장 정의 — 학습 코드는 ① 바탕 모델 불러오기 → ② 가까운 짝 데이터 준비 → ③ 채점표 켜기 → ④ 훈련사(SentenceTransformerTrainer)의 train()으로 가르치기, 이 네 단계로 돈다.

예시 폭격

예시 1 (완성예) — 바탕 모델 불러오기

# 필요한 라이브러리나 클래스를 가져와서 아래 예제에서 쓸 준비를 합니다.
from sentence_transformers import SentenceTransformer
# 한국어·영어·중국어에 두루 강한 모델
# 사용할 모델 이름을 지정해 어떤 LLM으로 답할지 정합니다.
model = SentenceTransformer('BAAI/bge-m3')

예시 2 (완성예) — 가까운 짝 데이터 준비

# 훈련 데이터는 표(Dataset) 하나로 만듭니다: pip install datasets
from datasets import Dataset
# anchor 열 = 앵커(질문), positive 열 = 가까운 짝(정답 문장)
train_dataset = Dataset.from_dict({
    "anchor":   ["AI란 무엇인가?", "딥러닝이란?"],
    "positive": ["AI는 인간 지능을 모방한 기술입니다.", "신경망을 여러 층 쌓아 학습하는 방법입니다."],
})

예시 3 (독립적용) — 학습 한 방 (분위기만)

# 훈련사에게 모델·데이터·채점표를 한 번에 맡깁니다.
from sentence_transformers import SentenceTransformerTrainer, SentenceTransformerTrainingArguments

trainer = SentenceTransformerTrainer(
    model=model,
    train_dataset=train_dataset,   # ②에서 만든 가까운 짝 표
    loss=loss,                     # ③에서 켠 채점표
    args=SentenceTransformerTrainingArguments(
        output_dir="./내모델",       # 다 배운 모델을 여기 저장
        num_train_epochs=3,         # 같은 데이터를 3바퀴 복습
        learning_rate=2e-5,         # 한 번에 고치는 정도 (작게)
    ),
)
trainer.train()
# epochs=복습 횟수, learning_rate=고치는 보폭. 숫자는 외울 필요 없음.

부록 박스 — 워밍업·에포크·학습률 (참고용, 건너뛰어도 됨)

epochs는 같은 데이터를 몇 바퀴 도는지(복습 횟수)다. lr(학습률, 2e-5)은 한 번에 모델을 고치는 보폭이다. 너무 크면 원래 실력이 무너지고, 너무 작으면 안 는다. warmup은 처음엔 살살(작은 보폭) 시작해 점점 보폭을 키우는 준비운동이다. 지금은 "복습 횟수와 보폭을 정한다"만 알면 충분하다.


개념 7 — 더 잘 가르치는 법: 배치 키우기와 하드 네거티브

망가지는 장면

기본대로 가르쳤더니 그럭저럭 됐다.

그런데 "심근경색"과 "협심증"처럼 비슷한데 다른 글은 여전히 헷갈린다.

자동으로 빌려 온 먼 짝들이 죄다 너무 동떨어진 글이라, 미묘한 차이를 배울 기회가 없었던 것이다.

일상비유 1 — 배치 키우기

소개팅 방에 사람이 많을수록, 한 주인공이 비교할 "안 맞는 사람"이 많아진다.

방에 4명이면 비교 대상 3명, 32명이면 31명이다.

비교 거리가 많을수록 눈이 더 빨리 트인다.

다만 방(GPU 메모리)이 좁으면 사람을 무한정 못 넣는다.

일상비유 2 — 하드 네거티브

자동으로 빌린 먼 짝은 너무 쉬운 오답이다("파이썬 vs 당뇨병").

여기에 헷갈리는 오답을 사람이 일부러 끼워 넣는다("당뇨병 vs 저혈당").

이렇게 일부러 고른 어려운 먼 짝이 하드 네거티브다.

쉬운 문제만 풀던 학생에게 함정 문제를 섞어 주는 것과 같다.

비유 코드 위험
방에 사람 많이 (배치↑) batch_size=32 → 자동 먼짝 31개 방(GPU) 좁으면 터짐
함정 문제 섞기 (하드 네거티브) texts=[앵커, 가까운짝, 헷갈리는먼짝] 손으로 골라야 해 품이 듦

한 문장 정의 — 배치를 키우면 자동 먼 짝이 늘어 성능이 오르고(GPU 한도 안에서), 거기에 주제는 비슷하지만 정답은 아닌 하드 네거티브를 사람이 끼워 넣으면 미묘한 차이까지 배운다.

예시 폭격

예시 1 (완성예) — 배치 크기와 자동 먼 짝 수

# 배치 4  → 앵커당 자동 먼 짝 3개
# 배치 32 → 앵커당 자동 먼 짝 31개
# 배치 128 → 앵커당 자동 먼 짝 127개  (단, GPU 메모리 한도 안에서)
# `batch_size`에 중간 결과를 담아 다음 줄에서 재사용합니다.
batch_size = 32

예시 2 (완성예) — 하드 네거티브 한 줄 (세 번째 열에 함정)

# 훈련 데이터표에 negative 열을 하나 더 두면 됩니다.
train_dataset = Dataset.from_dict({
    "anchor":   ["당뇨병의 증상은?"],                    # 앵커
    "positive": ["당뇨병 증상: 갈증·빈뇨·체중 감소."],     # 가까운 짝 (정답)
    "negative": ["저혈당 증상: 현기증·발한·불안감."],      # 하드 네거티브 (의료지만 정답 아님)
})

예시 3 (독립적용) — "비타민C 효능"이 앵커일 때, 쉬운 먼 짝과 하드 네거티브를 구분해 보자. - 쉬운(자동) 먼 짝: "자동차 타이어 교체법" (완전 무관) - 하드 네거티브: "비타민D 효능" (주제 비슷, 정답 아님 → 함정)

한 걸음 더 ▸ (지금 몰라도 됨)

성능을 더 짜내는 잔손질이 몇 개 더 있다. ① 학습 앵커를 실제 사용자가 던질 법한 질문으로 맞추기 ② 하드 네거티브 넉넉히 모으기 ③ 보폭(학습률) 바꿔 가며 제일 좋은 값 찾기 ④ 채점 강도 손잡이(scale) 조절. 지금은 "배치 키우기 + 하드 네거티브"만 들고 가면 된다.


개념 8 — 짝을 AI가 만든다: 합성 데이터 생성

망가지는 장면

우리 회사 문서 26개를 학습에 쓰려는데, 문서마다 어울리는 질문(앵커)이 없다.

질문을 손으로 다 만들자니 며칠이 걸린다.

가까운 짝(질문↔문서)이 없으면 학습 자체가 안 된다.

일상비유

문제집 출제를 AI에게 맡기는 것.

지문(문서)을 주고 "여기서 나올 만한 질문 2개 뽑아 줘"라고 하면, AI가 척척 만들어 준다.

그렇게 만든 (질문, 지문) 한 쌍이 곧 가까운 짝(포지티브)이다.

이렇게 AI로 자동 생성한 학습 데이터를 합성 데이터라 부른다.

비유 코드 위험
지문 주고 질문 뽑기 GPT에게 "이 문서로 질문 2개 만들어" 엉뚱한 질문 나오면 학습 망침
(질문, 문서) = 가까운 짝 all_queries, all_positive_docs 두 리스트 실제 사용자 질문과 결 다르면 효과↓

한 문장 정의 — 합성 데이터 생성은 우리 문서를 GPT에게 보여주고 "나올 법한 질문"을 자동으로 뽑게 해서, (질문, 문서) 가까운 짝을 손 안 대고 잔뜩 만드는 방법이다.

예시 폭격

예시 1 (완성예) — GPT에게 줄 부탁말 (프롬프트)

# 모델에게 어떤 역할과 입력을 줄지 프롬프트 틀을 만듭니다.
# 세 따옴표 안의 여러 줄은 줄바꿈까지 포함해 하나의 긴 프롬프트가 됩니다.
prompt = """다음 내용으로 낼 수 있는 질문을 2개 만들어 주세요.
질문만 쓰고 정답은 쓰지 않습니다.
본문을 못 본다고 가정하니 '위 본문을 바탕으로~'는 금지입니다.
1. (질문)
2. (질문)"""

예시 2 (worked) — 문서 한 조각 → 질문 2개

# 아래 예제는 핵심 흐름만 짧게 보여 줍니다.
# 문서: "2024년 일본 반도체 산업은 15% 성장, 정부는 300억 엔 추가 투자 발표."
# GPT가 만든 질문:
#   "2024년 일본 반도체 산업의 성장률은?"
#   "일본 정부의 반도체 추가 투자금액은?"
# → (질문, 문서) 두 쌍이 가까운 짝으로 생김

예시 3 (독립적용) — 문서가 26개고 문서당 질문 2개면, 가까운 짝은 몇 개? - 답: 26 × 2 = 52개. (책 실습도 52개 학습 데이터가 나온다)


개념 9 — 좋아졌는지 숫자로 재기: 검색 성능 점수

망가지는 장면

모델을 다 손봤다.

"좋아진 것 같긴 한데…" 느낌만 있다.

진짜 나아졌는지 숫자로 증명하지 못하면, 손본 게 헛수고였는지 알 수 없다.

일상비유

다이어트 전후 체중계.

"날씬해진 것 같아"는 못 믿는다.

68kg → 62kg 숫자가 찍혀야 효과를 안다.

검색 성능도 점수표로 전후를 잰다.

대표 점수 몇 개를 알아 두자(@5는 "상위 5개만 본다"는 뜻).

점수 이름 한 문장 뜻 묻는 질문
Accuracy@k 상위 k개 안에 정답이 있기만 하면 성공 "정답이 상위 k개 안에 있나?"
Precision@k 상위 k개 중 정답이 몇 할인가 "꺼내온 게 얼마나 알찬가?"
Recall@k 전체 정답 중 상위 k개로 몇 개나 건졌나 "정답을 안 놓쳤나?"
NDCG@k 정답이 위쪽에 있을수록 높은 점수 "정답이 윗줄에 있나?"
MRR@k 정답이 처음 나온 순위가 빠를수록 높음 "정답을 얼마나 빨리 찾나?"
MAP@k 정답 찾을 때마다의 정확도 평균 "전반적으로 꾸준히 정확한가?"
비유 코드 위험
전후 체중계 evaluator(원본모델) vs evaluator(손본모델) 안 재면 좋아졌는지 모름
점수표 한 장에 6개 InformationRetrievalEvaluator(...) 한 점수만 보면 착시 생김

한 문장 정의 — 검색 성능은 Accuracy·Precision·Recall·NDCG·MRR·MAP 같은 점수로 재며, 손보기 전 모델과 후 모델의 점수를 나란히 비교해 진짜 나아졌는지를 숫자로 증명한다.

예시 폭격

예시 1 (worked) — Accuracy@5 계산. 질문 3개, 각각 상위 5개 결과.

# 아래 예제는 핵심 흐름만 짧게 보여 줍니다.
# 질문1: [정답,오답,오답,오답,오답] → 포함(성공)
# 질문2: [오답,오답,정답,오답,오답] → 포함(성공)
# 질문3: [오답,오답,오답,오답,오답] → 미포함(실패)
# Accuracy@5 = 2/3 ≈ 0.667

예시 2 (worked) — MRR 계산. 정답이 처음 나온 순위의 역수 평균.

# 아래 예제는 핵심 흐름만 짧게 보여 줍니다.
# 질문1: 정답 1위 → 1/1 = 1.0
# 질문2: 정답 2위 → 1/2 = 0.5
# 질문3: 정답 3위 → 1/3 ≈ 0.33
# MRR = (1.0 + 0.5 + 0.33) / 3 ≈ 0.61

예시 3 (독립적용) — 전후 비교표 읽기. 손본 모델이 나아졌나?

# 아래 예제는 핵심 흐름만 짧게 보여 줍니다.
# 원본  모델: Recall@1 = 0.31
# 손본  모델: Recall@1 = 0.87
# → 0.31에서 0.87로 올랐으니 검색이 크게 좋아졌다 (증명 완료)

부록 박스 — 평가기 동작 순서 (참고용, 건너뛰어도 됨)

채점 도구(InformationRetrievalEvaluator)는 ① 질문과 모든 문서를 숫자로 바꾸고 ② 질문↔문서 코사인 유사도를 재고 ③ 유사도 높은 순으로 문서를 줄 세우고 ④ 진짜 정답이 몇 번째에 섰는지 보고 ⑤ 위의 6개 점수를 한 번에 뽑는다. 점수 계산은 도구가 알아서 하니, 우리는 전후 숫자만 비교하면 된다.


미니 시나리오 — 의료 RAG 검색을 손보는 하루

회사 의료 챗봇이 "심근경색"을 물으면 자꾸 엉뚱한 문서를 끌어온다.

순서대로 손봐 보자.

  1. 우리 의료 문서(PDF)를 잘라 26개 조각으로 만든다. (0장의 청크)
  2. 각 조각을 GPT에게 보여주고 "나올 법한 질문 2개"를 뽑게 한다 → 가까운 짝 52개 생김. (개념 8)
  3. 바탕 모델 BAAI/bge-m3를 불러온다. (개념 6)
  4. 채점표(MultipleNegativesRankingLoss)를 켠다. 먼 짝은 배치에서 자동으로 빌린다. (개념 4·5)
  5. "심근경색 vs 협심증" 같은 함정(하드 네거티브)을 몇 줄 끼워 넣는다. (개념 7)
  6. 훈련사 trainer.train()으로 가르친다. (개념 6)
  7. 손보기 전후 Recall@1을 잰다 → 0.31에서 0.87로 올랐다. 증명 끝. (개념 9)

이제 챗봇이 심근경색 질문에 제대로 된 문서를 꺼내 온다.


단순 규칙 (이것만 기억)

  • 일반 임베딩이 우리 분야에서 헛다리 짚으면 → 우리 문서로 다시 가르친다(파인튜닝).
  • 가르치는 법 → 가까운 짝은 가깝게, 먼 짝은 멀게(대조 학습).
  • 먼 짝은 → 손으로 안 만들고 배치에서 자동으로 빌린다. 가까운 짝만 준비하면 된다.
  • 가까운 짝이 없으면 → GPT에게 질문을 뽑게 해 만든다(합성 데이터).
  • 더 똑똑하게 → 배치 키우기 + 함정(하드 네거티브) 끼우기.
  • 좋아졌는지 → 전후 점수(Recall·MRR 등)를 숫자로 비교.

더 해보기

검증된 공식 자료(Tier 1)와 책 실습 노트북이다.

모델 이름·가격은 수시로 바뀐다. 책의 gpt-4o·BAAI/bge-m3 표기는 2025년 기준 예시이니, 실제로 쓸 땐 공식 목록을 확인하라: OpenAI 모델·HuggingFace 모델 허브. (검증 2026-05-21)



연습문제

  1. 설명. 임베딩 모델 길들이기의 핵심을 처음 듣는 사람에게 한 문장으로 설명하라.
  2. 구분. 두 개념(임베딩 모델, 생성 모델)을 실제 예시 하나로 구분하라.
  3. 적용. 내 프로젝트나 학습 노트에서 이 장의 개념을 적용해 작게 개선할 지점을 하나 고르라.

부록 A. 쉬운 용어 사전

용어 아주 쉬운 뜻 이 장에서 나온 위치
임베딩 모델 글이나 이미지를 검색하기 좋은 숫자 목록으로 바꾸는 모델. 부록 B와 본문 예시
생성 모델 질문과 맥락을 바탕으로 새 문장을 만들어 내는 모델. 부록 B와 본문 예시
하드 네거티브 정답처럼 헷갈리지만 실제로는 틀린 어려운 오답 예시. 부록 B와 본문 예시
무작위 네거티브 아무 관련 없는 오답 예시를 임의로 고른 것. 부록 B와 본문 예시

부록 B. 헷갈리는 개념 비교표

A B 구분 포인트
임베딩 모델 생성 모델 임베딩 모델은 찾기 좋은 숫자를 만들고, 생성 모델은 답 문장을 만든다.
하드 네거티브 무작위 네거티브 하드 네거티브는 헷갈리는 오답이라 검색 모델을 더 세게 훈련한다.

부록 C. 더 읽을 자료

  • 이 장의 더 해보기 섹션 — 이미 모아 둔 공식 문서나 실습 링크가 있으면 여기서 먼저 확인한다.
  • 같은 책의 0장 한눈에 보기 — 용어가 막히면 0장의 용어집과 개념 척추로 돌아간다.
  • 원본 딥다이브판 같은 장 — 입문판을 읽고 큰 흐름이 잡힌 뒤 세부 논리를 더 깊게 확인한다.
  • 이 장의 flashcards.json — 읽은 직후 질문만 보고 답을 떠올리는 회상 연습에 쓴다.

부록 D. 연습문제 풀이

  1. 설명 예시. 임베딩 모델 길들이기는 RAG에서 자료를 더 잘 찾고, 근거를 더 안전하게 붙이고, 답변 흐름을 더 다루기 쉽게 만드는 방법을 보는 장이다. 중요한 것은 용어를 외우는 것이 아니라, 이 개념이 어떤 입력·부품·결정에 영향을 주는지 말로 풀어 보는 것이다.
  2. 구분 예시. 두 개념(임베딩 모델, 생성 모델)의 차이는 이렇게 잡으면 된다. 임베딩 모델은 찾기 좋은 숫자를 만들고, 생성 모델은 답 문장을 만든다. 실제 사례를 볼 때는 목적, 입력, 실패했을 때의 증상을 따로 적어 보면 헷갈리지 않는다.
  3. 적용 예시. 가장 작은 개선부터 고른다. 예를 들어 이름을 더 분명히 하거나, 평가 기준을 한 줄 추가하거나, 직접 알 필요 없는 내부 정보를 감추는 식으로 시작한다. 한 번에 크게 갈아엎는 것보다 작은 변경 하나를 확인하며 진행하는 쪽이 입문 단계에 맞다.

다음 장 예고

지금까지 검색 부품(임베딩 모델)을 우리 분야에 맞게 길들이는 법을 봤다.

다음 장에서는 글뿐 아니라 이미지·표까지 함께 다루는 RAG로 넘어간다.

지금은 "우리 문서로 임베딩을 다시 가르친다"만 머리에 있으면 충분하다.

최신 동향 (검증 2026-07-09) — 책의 InputExample + model.fit 학습 방식은 sentence-transformers v3(2024)부터 은퇴 수순이고, 현행 표준은 본문에 적은 Dataset 표 + SentenceTransformerTrainer 방식이다 — 공식 훈련 가이드.

난이도
에피소드
질문
카드를 로딩 중...
답변

클릭하거나 Space를 눌러 뒤집기

0 / 0
학습 진도 0%
이동   Space 뒤집기   R 셔플